package com.management.department.teacherinfosystem.Controller;

import com.management.department.teacherinfosystem.Entity.Attachment;
import com.management.department.teacherinfosystem.Entity.Textbook;
import com.management.department.teacherinfosystem.Entity.TextbookAuthor;
import com.management.department.teacherinfosystem.Entity.User;
import com.management.department.teacherinfosystem.Repository.AttachmentRepository;
import com.management.department.teacherinfosystem.Repository.TextbookAuthorRepository;
import com.management.department.teacherinfosystem.Repository.TextbookRepository;
import com.management.department.teacherinfosystem.Repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/api/textbooks")
@RequiredArgsConstructor
public class TextbookController {

    private final TextbookRepository textbookRepository;
    private final UserRepository userRepository;
    private final AttachmentRepository attachmentRepository;
    private final TextbookAuthorRepository authorRepository;

    @Value("${file.upload.academic-dir:uploads/academic_achievements/}")
    private String uploadDir;

    private User currentUser() {
        org.springframework.security.core.Authentication auth = org.springframework.security.core.context.SecurityContextHolder.getContext().getAuthentication();
        String username = auth.getName();
        return userRepository.findByUsernameWithRoles(username).orElse(null);
    }

    private boolean currentIsAdmin() {
        org.springframework.security.core.Authentication auth = org.springframework.security.core.context.SecurityContextHolder.getContext().getAuthentication();
        return auth.getAuthorities().stream().anyMatch(a -> {
            String au = a.getAuthority();
            return "ROLE_ADMIN".equalsIgnoreCase(au) || "ADMIN".equalsIgnoreCase(au);
        });
    }

    private File ensureDir(String base) {
        if (!StringUtils.hasText(base)) base = "uploads/textbook_attachments/";
        if (!base.endsWith("/") && !base.endsWith("\\")) base = base + "/";
        File dir = new File(base);
        if (!dir.exists()) dir.mkdirs();
        return dir;
    }

    /* ===== staff ===== */
    @GetMapping("/mine")
    public ResponseEntity<?> myList() {
        User u = currentUser();
        if (u == null) return ResponseEntity.status(401).body("未登录");
        List<Textbook> list = textbookRepository.findByUser(u);
        return ResponseEntity.ok(list.stream().map(this::dto).toList());
    }

    @PostMapping
    public ResponseEntity<?> create(@RequestBody Map<String, Object> payload) {
        User u = currentUser();
        if (u == null) return ResponseEntity.status(401).body("未登录");
        Textbook t = new Textbook();
        t.setUser(u);
        fillFromPayload(t, payload);
        upsertAuthors(t, payload.get("authors"));
        t.setStatus("PENDING");
        textbookRepository.save(t);
        return ResponseEntity.ok(Map.of("id", t.getId()));
    }

    @PutMapping("/{id}")
    public ResponseEntity<?> update(@PathVariable("id") Long id, @RequestBody Map<String, Object> payload) {
        User u = currentUser();
        if (u == null) return ResponseEntity.status(401).body("未登录");
        Textbook t = textbookRepository.findById(id).orElse(null);
        if (t == null) return ResponseEntity.notFound().build();
        if (!currentIsAdmin() && !Objects.equals(t.getUser().getId(), u.getId())) return ResponseEntity.status(403).body("无权编辑");
        fillFromPayload(t, payload);
        if (!currentIsAdmin()) {
            t.setStatus("PENDING");
            t.setReviewOpinion(null);
            t.setReviewerId(null);
            t.setReviewTime(null);
        }
        upsertAuthors(t, payload.get("authors"));
        textbookRepository.save(t);
        return ResponseEntity.ok().build();
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<?> delete(@PathVariable("id") Long id) {
        User u = currentUser();
        if (u == null) return ResponseEntity.status(401).body("未登录");
        Textbook t = textbookRepository.findById(id).orElse(null);
        if (t == null) return ResponseEntity.notFound().build();
        if (!currentIsAdmin() && !Objects.equals(t.getUser().getId(), u.getId())) return ResponseEntity.status(403).body("无权删除");
        textbookRepository.deleteById(id);
        authorRepository.deleteByTextbookId(id);
        attachmentRepository.deleteByRelatedIdAndRelatedType(id, "textbook");
        return ResponseEntity.ok().build();
    }

    /* ===== admin ===== */
    @GetMapping("/all")
    public ResponseEntity<?> listAll(@RequestParam(value = "textbookType", required = false) String textbookType,
                                     @RequestParam(value = "level", required = false) String level,
                                     @RequestParam(value = "status", required = false) String status,
                                     @RequestParam(value = "page", required = false) Integer page,
                                     @RequestParam(value = "size", required = false) Integer size) {
        if (!currentIsAdmin()) return ResponseEntity.status(403).body("权限不足");
        List<Textbook> list = textbookRepository.findAllWithUser();
        List<Map<String, Object>> filtered = list.stream().filter(t -> {
            if (textbookType != null && !textbookType.isEmpty() && !textbookType.equals(t.getTextbookType())) return false;
            if (level != null && !level.isEmpty() && !level.equals(t.getLevel())) return false;
            if (status != null && !status.isEmpty() && !status.equals(t.getStatus())) return false;
            return true;
        }).map(this::dtoWithUser).collect(Collectors.toList());
        int total = filtered.size();
        List<Map<String, Object>> pageList = applyPaging(filtered, page, size);
        if (page == null || size == null) return ResponseEntity.ok(filtered);
        return ResponseEntity.ok(Map.of("total", total, "list", pageList));
    }

    @GetMapping("/pending")
    public ResponseEntity<?> listPending(@RequestParam(value = "textbookType", required = false) String textbookType,
                                         @RequestParam(value = "level", required = false) String level,
                                         @RequestParam(value = "page", required = false) Integer page,
                                         @RequestParam(value = "size", required = false) Integer size) {
        return listAll(textbookType, level, "PENDING", page, size);
    }

    @PostMapping("/{id}/review")
    @Transactional
    public ResponseEntity<?> review(@PathVariable("id") Long id, @RequestBody Map<String, Object> payload) {
        if (!currentIsAdmin()) return ResponseEntity.status(403).body("权限不足");
        String action = String.valueOf(payload.get("action"));
        String opinion = (String) payload.getOrDefault("opinion", null);
        Textbook t = textbookRepository.findById(id).orElse(null);
        if (t == null) return ResponseEntity.notFound().build();
        t.setStatus("APPROVED".equalsIgnoreCase(action) ? "APPROVED" : "REJECTED");
        t.setReviewOpinion(opinion);
        t.setReviewerId(Optional.ofNullable(currentUser()).map(User::getId).orElse(null));
        t.setReviewTime(LocalDateTime.now());
        textbookRepository.save(t);
        return ResponseEntity.ok().build();
    }

    /* ===== 附件 ===== */
    @PostMapping("/{id}/attachments")
    public ResponseEntity<?> upload(@PathVariable("id") Long id,
                                    @RequestPart("file") MultipartFile file,
                                    @RequestPart(value = "fileType", required = false) String fileType) throws Exception {
        User u = currentUser();
        if (u == null) return ResponseEntity.status(401).body("未登录");
        Textbook t = textbookRepository.findById(id).orElse(null);
        if (t == null) return ResponseEntity.notFound().build();
        File dir = ensureDir(uploadDir);
        String original = Objects.requireNonNull(file.getOriginalFilename());
        String ext = original.contains(".") ? original.substring(original.lastIndexOf('.')) : "";
        String save = System.currentTimeMillis() + "_" + UUID.randomUUID() + ext;
        file.transferTo(new File(dir, save));
        Attachment att = new Attachment();
        att.setFileName(original);
        att.setFileUrl(dir.getPath() + File.separator + save);
        att.setRelatedType("textbook");
        att.setRelatedId(id);
        att.setUploadedBy(u.getId());
        att.setUploadedAt(new Date());
        attachmentRepository.save(att);
        return ResponseEntity.ok(Map.of("url", att.getFileUrl()));
    }

    @GetMapping("/{id}/attachments")
    public List<Attachment> listAttachments(@PathVariable("id") Long id) {
        return attachmentRepository.findByRelatedTypeAndRelatedId("textbook", id);
    }

    @DeleteMapping("/{id}/attachments/{attId}")
    public ResponseEntity<?> deleteAttachment(@PathVariable("id") Long id, @PathVariable("attId") Long attId) {
        attachmentRepository.deleteById(attId);
        return ResponseEntity.ok().build();
    }

    @GetMapping("/{id}/attachments/{attId}/download")
    public ResponseEntity<Resource> download(@PathVariable("id") Long id, @PathVariable("attId") Long attId) {
        Attachment att = attachmentRepository.findById(attId).orElse(null);
        if (att == null || !Objects.equals(att.getRelatedId(), id) || !"textbook".equals(att.getRelatedType())) return ResponseEntity.notFound().build();
        File file = new File(att.getFileUrl());
        if (!file.exists()) return ResponseEntity.notFound().build();
        String encoded = java.net.URLEncoder.encode(att.getFileName(), StandardCharsets.UTF_8).replaceAll("\\+", "%20");
        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename*=UTF-8''" + encoded)
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .body(new FileSystemResource(file));
    }

    @GetMapping("/{id}/authors")
    public ResponseEntity<?> listAuthors(@PathVariable("id") Long id) {
        Textbook t = textbookRepository.findById(id).orElse(null);
        if (t == null) return ResponseEntity.notFound().build();
        return ResponseEntity.ok(authorRepository.findByTextbook_Id(id));
    }

    /* ===== 工具 ===== */
    private void fillFromPayload(Textbook t, Map<String, Object> p) {
        t.setTitle((String) p.get("title"));
        t.setIsbn((String) p.getOrDefault("isbn", null));
        t.setPublisher((String) p.getOrDefault("publisher", null));
        t.setPublishDate(Optional.ofNullable((String) p.get("publish_date")).map(LocalDate::parse).orElse(null));
        t.setWordCount(optInt(p.get("word_count")));
        t.setEditorRole((String) p.getOrDefault("editor_role", null));
        t.setInCampusRank(optInt(p.get("in_campus_rank")));
        t.setTextbookType((String) p.getOrDefault("textbook_type", null));
        t.setLevel((String) p.getOrDefault("level", null));
        t.setReprintInfo((String) p.getOrDefault("reprint_info", null));
    }

    private Integer optInt(Object v) { try { return v == null ? null : Integer.valueOf(String.valueOf(v)); } catch (Exception e) { return null; } }

    @SuppressWarnings("unchecked")
    private void upsertAuthors(Textbook t, Object authorsObj) {
        if (authorsObj == null) return;
        try {
            List<Map<String, Object>> authors = (List<Map<String, Object>>) authorsObj;
            // 清空旧作者后重建（简单幂等）
            if (t.getId() != null) authorRepository.deleteByTextbookId(t.getId());
            for (Map<String, Object> a : authors) {
                TextbookAuthor ta = new TextbookAuthor();
                ta.setTextbook(t);
                ta.setAuthorName(String.valueOf(a.getOrDefault("author_name", a.getOrDefault("authorName", ""))));
                ta.setInCampusRank(optInt(a.get("in_campus_rank")) != null ? optInt(a.get("in_campus_rank")) : optInt(a.get("inCampusRank")));
                ta.setRole((String) a.getOrDefault("role", null));
                ta.setWordCount(optInt(a.get("word_count")) != null ? optInt(a.get("word_count")) : optInt(a.get("wordCount")));
                if (ta.getAuthorName() != null && !ta.getAuthorName().isBlank()) {
                    authorRepository.save(ta);
                }
            }
        } catch (Exception ignored) {}
    }

    private Map<String, Object> dto(Textbook t) {
        Map<String, Object> m = new LinkedHashMap<>();
        m.put("id", t.getId());
        m.put("title", t.getTitle());
        m.put("isbn", t.getIsbn());
        m.put("publisher", t.getPublisher());
        m.put("publishDate", t.getPublishDate());
        m.put("wordCount", t.getWordCount());
        m.put("editorRole", t.getEditorRole());
        m.put("inCampusRank", t.getInCampusRank());
        m.put("textbookType", t.getTextbookType());
        m.put("level", t.getLevel());
        m.put("reprintInfo", t.getReprintInfo());
        m.put("status", t.getStatus());
        return m;
    }

    private Map<String, Object> dtoWithUser(Textbook t) {
        Map<String, Object> m = dto(t);
        m.put("userId", t.getUser() != null ? t.getUser().getId() : null);
        m.put("userName", t.getUser() != null ? t.getUser().getName() : null);
        return m;
    }

    private <T> List<T> applyPaging(List<T> list, Integer page, Integer size) {
        if (page == null || size == null || page < 1 || size < 1) return list;
        int from = Math.min((page - 1) * size, list.size());
        int to = Math.min(from + size, list.size());
        return list.subList(from, to);
    }
} 